`timescale 1ns / 1ps
`include "const.v"
module CPU(
    input clk,
    input reset,
    output [31:0] C_PC,
    output [31:0] C_addr,
    output C_MemWr,
    output [1:0] C_store,
    output [31:0] C_Dout,
    input [31:0] C_Din,
    input [5:0] C_IRQ
    );

    wire [31:0] macroPC;
    //MUXs
    wire [31:0] MUXPC, MUX_D_F1, MUX_D_F2, MUX_E_F1, MUX_E_F2, MUX_E_ASrc, MUX_E_MDV, MUX_M_F2, MUX_W_Rin;
    wire [31:0] MUX_NPC, MUX_CP0PC;
    wire [4:0] MUX_D_A3;
    //F 
    wire [31:0] F_addr, F_instr;
    wire F_exc, IFExc;
	 wire [1:0] D_PCSrcp;
    wire [4:0] F_ExcCode;
    //D
    wire D_RegWr, D_ASrc, D_MemWr, MDCmd, D_start, D_HLWr, D_MDDst, D_HLSrc, D_shmt, D_ld, D_st, D_Stall, D_CO;
    wire [1:0] D_Tu1, D_Tu2, D_Tn, D_store, D_Mop, D_RegDst, D_PCSrc, D_Eop;
    wire [2:0] D_FA1, D_FA2, D_Dop, D_RinSel, D_Cop;
    wire [3:0] D_Aop;
    wire [4:0] D_A1, D_A2, D_A3, D_shamt, D_RS, D_RT, D_RD;
    wire [15:0] D_imm16;
    wire [25:0] D_imm26;
    wire [31:0] D_imm32, D_V1, D_V2, D_PC, D_instr;
    wire isRI;
    wire D_exc;
    wire [4:0] D_ExcCode;
    wire D_CP0Wr, D_eret;
    wire D_isBD;
    //E
    wire E_RegWr, E_ASrc, E_MemWr, E_MDCmd, E_shmt, E_ld, E_st, E_start, E_busy, E_HLWr, E_MDDst, E_HLSrc;
    wire [1:0] E_RegDst, E_Tu1, E_Tu2, E_Tn, E_store, E_Mop;
    wire [2:0] E_RinSel, E_Dop, E_FA1, E_FA2;
	wire [3:0] E_Aop;
    wire [4:0] E_A1, E_A2, E_A3, E_shamt;
    wire [31:0] E_AO, E_V1, E_V2, E_imm32, E_PC, HI, LO, E_instr;
    wire E_exc;
    wire [4:0] E_ExcCode;
    wire Ov;
    wire [4:0] ECode;
    wire E_CP0Wr, E_eret;
    wire E_isBD;
    //M
    wire M_RegWr, M_MemWr, M_ld, M_st, M_FA1, M_FA2;
    wire [1:0] M_Tu1, M_Tu2, M_Tn, M_store;
	wire [2:0] M_Dop, M_RinSel;
    wire [4:0] M_A1, M_A2, M_A3;
    wire [31:0] M_MDV, M_AO, M_V2, M_PC, M_DR, M_instr;
    wire m_exc;
    wire [4:0] m_ExcCode;
    wire M_exc;
    wire [4:0] M_ExcCode;
    wire M_CP0Wr, M_eret;
    wire CP0_IntReq;
    wire [31:0] CP0_EPC, CP0_Dout;
    wire M_isBD, CP0_eret;
    //W
    wire W_RegWr;
    wire [1:0] W_Tu1, W_Tu2, W_Tn;
    wire [2:0] W_RinSel, W_Dop;
    wire [4:0] W_A1, W_A2, W_A3;
    wire [31:0] W_AO, W_DR, W_PC, W_DEO, W_MDV, W_instr, W_CP0_Dout;

//modules
//        
    //F
    PC pc(
        .clk(clk), .reset(reset), .en(CP0_eret | (~D_Stall) | CP0_IntReq),
        .NPC(MUX_NPC), .addr(F_addr)
    );
    IM im(.addr(F_addr), .instr(F_instr));

    //ppl
    IF_ID if_id(
        .clk(clk), .reset(reset | CP0_IntReq | CP0_eret), .en(~D_Stall),
        .F_instr(IFExc? 0 :F_instr), .F_PC(F_addr),
        .F_isBD((|D_PCSrcp) && (~CP0_eret)),
        .D_RS(D_RS), .D_RT(D_RT), .D_RD(D_RD),
        .D_imm16(D_imm16), .D_PC(D_PC),
        .D_imm26(D_imm26), .D_instr(D_instr),
        .D_shamt(D_shamt),
        .D_isBD(D_isBD)
    );

    //D
    GRF grf(
        .clk(clk), .reset(reset),
        .A1(D_RS), .A2(D_RT), .V1(D_V1), .V2(D_V2),
        .en(W_RegWr), .addr(W_PC),
        .A3(W_A3), .Rin(MUX_W_Rin)
    );
    EXT ext(
        .imm16(D_imm16), .imm32(D_imm32), .Eop(D_Eop)
    );
    CMP cmp(
        .CA(MUX_D_F1), .CB(MUX_D_F2), .Cop(D_Cop), .CO(D_CO)
    );
    MainCtrl mainCtrl(
        .instr(D_instr),
        .RinSel(D_RinSel), .RegWr(D_RegWr), .RegDst(D_RegDst),
        .PCSrc(D_PCSrc),
        .Eop(D_Eop), .Cop(D_Cop), .Aop(D_Aop),
        .ASrc(D_ASrc), .MemWr(D_MemWr),
        .store(D_store), .Dop(D_Dop),
        .start(D_start), .Mop(D_Mop),
        .MDCmd(MDCmd), .HLWr(D_HLWr),
        .MDDst(D_MDDst), .HLSrc(D_HLSrc),
        .shmt(D_shmt), .ld(D_ld), .st(D_st),
        .CP0Wr(D_CP0Wr), .eret(D_eret)
    );
    AT at(
        .instr(D_instr),
        .A1(D_A1), .A2(D_A2), .A3(D_A3),
        .Tu1(D_Tu1), .Tu2(D_Tu2), .Tn(D_Tn),
        .isRI(isRI)
    );
    Harz_D hzD(
        .A1(D_A1), .A2(D_A2),
        .Tu1(D_Tu1), .Tu2(D_Tu2),
        .E_A3(E_A3), .E_RegWr(E_RegWr), .E_Tn(E_Tn), .E_RinSel(E_RinSel),
        .M_A3(M_A3), .M_RegWr(M_RegWr), .M_Tn(M_Tn), .M_RinSel(M_RinSel),
        .D_FA1(D_FA1), .D_FA2(D_FA2), .D_Stall(D_Stall),
        .E_start(E_start), .E_busy(E_busy), .MDCmd(MDCmd)
    );

    //ppl
    ID_EX id_ex(
        .clk(clk), .reset(reset | CP0_IntReq | CP0_eret), .stall(D_Stall),
        .D_V1(MUX_D_F1), .D_V2(MUX_D_F2), .D_imm32(D_imm32), .D_PC(D_PC),
        .D_RinSel(D_RinSel), .D_RegWr(D_RegWr),
        .D_Aop(D_Aop), .D_ASrc(D_ASrc), .D_MemWr(D_MemWr),
        .D_A1(D_A1), .D_A2(D_A2),.D_Tu1(D_Tu1), .D_Tu2(D_Tu2),
        .D_A3(D_A3), .D_Tn(D_Tn),
        .D_store(D_store), .D_Dop(D_Dop), .D_start(D_start), .D_Mop(D_Mop),
        .D_HLWr(D_HLWr), .D_MDDst(D_MDDst), .D_HLSrc(D_HLSrc),
        .D_shmt(D_shmt), .D_shamt(D_shamt),
        .D_ld(D_ld), .D_st(D_st), .D_instr(D_instr),
        .D_CP0Wr(D_CP0Wr), .D_eret(D_eret),
        .D_isBD(D_isBD),
        .E_V1(E_V1), .E_V2(E_V2), .E_imm32(E_imm32), .E_PC(E_PC),
        .E_RinSel(E_RinSel), .E_RegWr(E_RegWr),
        .E_Aop(E_Aop), .E_ASrc(E_ASrc), .E_MemWr(E_MemWr),
        .E_A1(E_A1), .E_A2(E_A2),.E_Tu1(E_Tu1), .E_Tu2(E_Tu2),
        .E_A3(E_A3), .E_Tn(E_Tn),
        .E_store(E_store), .E_Dop(E_Dop), .E_start(E_start), .E_Mop(E_Mop),
        .E_HLWr(E_HLWr), .E_MDDst(E_MDDst), .E_HLSrc(E_HLSrc),
        .E_shmt(E_shmt), .E_shamt(E_shamt),
        .E_ld(E_ld), .E_st(E_st), .E_instr(E_instr),
        .E_CP0Wr(E_CP0Wr), .E_eret(E_eret),
        .E_isBD(E_isBD)
    );

    //E
    ALU alu(
        .AA(MUX_E_F1), .AB(MUX_E_ASrc),
        .Aop(E_Aop), .shmt(E_shmt), .shamt(E_shamt),
        .AO(E_AO), .Ov(Ov)
    );
    MDU mdu(
        .clk(clk), .reset(reset),
        .A(MUX_E_F1), .B(MUX_E_F2),
        .start(E_start && (~CP0_IntReq) && (~CP0_eret)), .Mop(E_Mop),
        .MDDst(E_MDDst), .HLWr(E_HLWr),
        .busy(E_busy), .HI(HI), .LO(LO)
    );
    Harz_E hzE(
        .E_A1(E_A1), .E_A2(E_A2),
        .E_Tu1(E_Tu1), .E_Tu2(E_Tu2),
        .M_A3(M_A3), .M_RegWr(M_RegWr), .M_Tn(M_Tn), .M_RinSel(M_RinSel),
        .W_A3(W_A3), .W_RegWr(W_RegWr), .W_Tn(W_Tn),
        .E_FA1(E_FA1), .E_FA2(E_FA2)
    );

    //ppl
    EX_MEM ex_mem(
        .clk(clk), .reset(reset | CP0_IntReq | CP0_eret),
        .E_AO(E_AO), .E_V2(MUX_E_F2), .E_PC(E_PC),
        .E_RinSel(E_RinSel), .E_RegWr(E_RegWr), .E_MemWr(E_MemWr),
        .E_A1(E_A1), .E_Tu1(E_Tu1), .E_A2(E_A2), .E_Tu2(E_Tu2),
        .E_A3(E_A3), .E_Tn(E_Tn),
        .E_store(E_store), .E_Dop(E_Dop), .E_MDV(MUX_E_MDV),
        .E_ld(E_ld), .E_st(E_st), .E_instr(E_instr),
        .E_CP0Wr(E_CP0Wr), .E_eret(E_eret),
        .E_isBD(E_isBD),
        .M_AO(M_AO), .M_V2(M_V2), .M_PC(M_PC),
        .M_RinSel(M_RinSel), .M_RegWr(M_RegWr), .M_MemWr(M_MemWr),
        .M_A1(M_A1), .M_Tu1(M_Tu1), .M_A2(M_A2), .M_Tu2(M_Tu2),
        .M_A3(M_A3), .M_Tn(M_Tn),
        .M_store(M_store), .M_Dop(M_Dop), .M_MDV(M_MDV),
        .M_ld(M_ld), .M_st(M_st), .M_instr(M_instr),
        .M_CP0Wr(M_CP0Wr), .M_eret(M_eret),
        .M_isBD(M_isBD)
    );

    assign macroPC = (M_PC ? M_PC :
                      E_PC ? E_PC :
                      D_PC ? D_PC :
                      F_addr);
    assign C_PC = macroPC;
    assign C_addr = M_AO;
    assign C_MemWr = M_MemWr & (~CP0_IntReq);
    assign C_store = M_store;
    assign C_Dout = MUX_M_F2;
    assign M_DR = C_Din;

    Harz_M hzM(
        .M_A1(M_A1), .M_Tu1(M_Tu1),
        .M_A2(M_A2), .M_Tu2(M_Tu2),
        .W_A3(W_A3), .W_RegWr(W_RegWr), .W_Tn(W_Tn),
        .M_FA1(M_FA1), .M_FA2(M_FA2)
    );

    //ppl
    MEM_WB mem_rb(
        .clk(clk), .reset(reset | CP0_IntReq),
        .M_AO(M_AO), .M_DR(M_DR), .M_PC(M_PC),
        .M_RinSel(M_RinSel), .M_RegWr(M_RegWr),
        .M_A1(M_A1), .M_A2(M_A2),
        .M_Tu1(M_Tu1), .M_Tu2(M_Tu2),
        .M_A3(M_A3), .M_Tn(M_Tn),
        .M_Dop(M_Dop),.M_MDV(M_MDV), .M_instr(M_instr),
        .M_CP0_Dout(CP0_Dout),
        .W_AO(W_AO), .W_DR(W_DR), .W_PC(W_PC),
        .W_RinSel(W_RinSel), .W_RegWr(W_RegWr),
        .W_A1(W_A1), .W_A2(W_A2),
        .W_Tu1(W_Tu1), .W_Tu2(W_Tu2),
        .W_A3(W_A3), .W_Tn(W_Tn),
        .W_Dop(W_Dop), .W_MDV(W_MDV), .W_instr(W_instr),
        .W_CP0_Dout(W_CP0_Dout)
    );
    DREXT drext(.AO(W_AO), .DR(W_DR),.Dop(W_Dop), .DEO(W_DEO));

    //W
    //none

    // MUX
    assign D_PCSrcp = (D_PCSrc == 2'b01 && D_CO == 0)? 0 : D_PCSrc;
    assign MUXPC = (D_PCSrcp == 2'b00)? (F_addr + 4) :
                   (D_PCSrcp == 2'b01)? (D_PC + 4 + {{14{D_imm16[15]}}, D_imm16, 2'b0}) :
                   (D_PCSrcp == 2'b10)? ({14'b0, D_imm26, 2'b0}) : 
                                        MUX_D_F1;
    assign MUX_NPC = (CP0_IntReq | CP0_eret)? MUX_CP0PC : MUXPC;
    assign MUX_CP0PC = (CP0_IntReq? 32'h4180 : CP0_EPC);
    assign MUX_D_F1 = (D_FA1 == 3'b000)? D_V1 :
                      (D_FA1 == 3'b001)? E_imm32 :
                      (D_FA1 == 3'b010)? (E_PC + 8) :
                      (D_FA1 == 3'b011)? M_AO :
                      (D_FA1 == 3'b100)? (M_PC + 8) :
                      (D_FA1 == 3'b101)? M_MDV :
                                         0;
    assign MUX_D_F2 = (D_FA2 == 3'b000)? D_V2 :
                      (D_FA2 == 3'b001)? E_imm32 :
                      (D_FA2 == 3'b010)? (E_PC + 8) :
                      (D_FA2 == 3'b011)? M_AO :
                      (D_FA2 == 3'b100)? (M_PC + 8) :
                      (D_FA2 == 3'b101)? M_MDV :
                                         0;
    assign MUX_E_F1 = (E_FA1 == 3'b000)? E_V1 :
                      (E_FA1 == 3'b001)? M_AO :
                      (E_FA1 == 3'b010)? (M_PC + 8) :
                      (E_FA1 == 3'b011)? MUX_W_Rin :
                      (E_FA1 == 3'b100)? M_MDV :
                                         0;
    assign MUX_E_F2 = (E_FA2 == 3'b000)? E_V2 :
                      (E_FA2 == 3'b001)? M_AO :
                      (E_FA2 == 3'b010)? (M_PC + 8) :
                      (E_FA2 == 3'b011)? MUX_W_Rin :
                      (E_FA2 == 3'b100)? M_MDV :
                                         0;
    assign MUX_E_ASrc = (E_ASrc == 1)? E_imm32 : MUX_E_F2;
    assign MUX_E_MDV = (E_HLSrc == 1)? HI: LO;
    assign MUX_M_F2 = (M_FA2 == 0)? M_V2: MUX_W_Rin;
    assign MUX_W_Rin = (W_RinSel == 3'b000)? W_AO : 
                       (W_RinSel == 3'b001)? W_DEO : 
                       (W_RinSel == 3'b010)? W_MDV : 
                       (W_RinSel == 3'b011)? (W_PC + 8) : 
                       (W_RinSel == 3'b100)? W_CP0_Dout : 
                                             0;
    assign MUX_D_A3 = (D_RegDst == 2'b00)? D_instr[20:16] :
                      (D_RegDst == 2'b01)? D_instr[15:11] :
                      (D_RegDst == 2'b10)? 5'b11111 :
                                           0;

    ////// P7
    assign IFExc = (MUX_NPC[1:0] || MUX_NPC < 32'h3000 || MUX_NPC > 32'h4ffc);
    ExcReg erF(     
        .clk(clk), .reset(reset | CP0_IntReq | CP0_eret), .en(~D_stall),
        .prevExc(1'b0), .prevExcCode(5'b0),
        .thisExc(IFExc), .thisExcCode(`AdEL),
        .exc(F_exc), .ExcCode(F_ExcCode)
    );
    ExcReg erD(
        .clk(clk), .reset(reset | CP0_IntReq | CP0_eret), .en(1'b1),
        .prevExc(F_exc), .prevExcCode(F_ExcCode),
        .thisExc(isRI), .thisExcCode(`RI),
        .exc(D_exc), .ExcCode(D_ExcCode)
    );
	assign ECode = (M_ld ? `AdEL : M_MemWr ? `AdES : `Ov);
    ExcReg erE(
        .clk(clk), .reset(reset | CP0_IntReq | CP0_eret), .en(1'b1),
        .prevExc(D_exc), .prevExcCode(D_ExcCode),
        .thisExc(Ov), .thisExcCode(ECode),
        .exc(E_exc), .ExcCode(E_ExcCode)
    );
    MExcDetect mDet(
        .MemWr(M_MemWr), .store(M_store),
        .Dop(M_Dop), .ld(M_ld),  .AO(M_AO),
        .isExc(m_exc), .ExcCode(m_ExcCode)
    );

    assign M_ExcCode = (E_exc? E_ExcCode : m_ExcCode);
    assign  M_exc = m_exc | E_exc;

    CP0 cp0(
        .clk(clk), .reset(reset), 
        .A1(M_instr[`RD]), .A2(M_instr[`RD]),
        .Din(MUX_M_F2), .PC(macroPC), 
        .ExcCode(M_ExcCode), .HWInt(C_IRQ), .isBD(M_isBD),
        .We(M_CP0Wr), .EXLSet(M_exc), .EXLClr(M_eret),
        .IntReq(CP0_IntReq), .eret(CP0_eret),
        .EPC(CP0_EPC), .Dout(CP0_Dout)
    );

endmodule
